using FileServer.Abstractions.Models;
using FileServer.Abstractions.Models.Entities;
using FileServer.Abstractions.Models.Options;

namespace FileServer.Extension.Providers;

/// <summary>
/// 本地文件的操作工具类
/// </summary>
public class LocalFileUtil
{
    public static async Task<FileResource> CreateLocalFile(Stream file, UploadFileInputBase input,
        Abstractions.Models.Options.FileServerOptions options)
    {


        string tmpFilePath = string.Empty;
        if (file != null)
        {
            tmpFilePath = await ReceiveToTempFileAsync(file, options);
        }

        if (string.IsNullOrEmpty(tmpFilePath))
        {
            throw new FileNotFoundException("file does not exist");
        }

        var fileResource = new FileResource();
        fileResource.Id = "";
        fileResource.FileNo = GenerateNumber();
        fileResource.Length = new FileInfo(tmpFilePath).Length;
        fileResource.OriginName = input.FileName;
        fileResource.IsTemp = input.IsTemp;
        fileResource.CreateTime = DateTime.Now;
        fileResource.PeriodMinute = input.PeriodMinute;
        fileResource.Path = tmpFilePath;
        fileResource.StorageKey = input.StorageKey;
        fileResource.ProviderType = "local";
        return fileResource;
    }

    public static async Task<FileResource> CreatePartLocalFile(Stream file, UploadPartInputStream input,
       Abstractions.Models.Options.FileServerOptions options, FileResource fileResource = null)
    {
        string id = input.UploadId;
        if (string.IsNullOrEmpty(input.UploadId))
        {
            id = Guid.NewGuid().ToString().Replace("-", "");
            input.UploadId = id;
        }
        string tmpFilePath = string.Empty;
        if (file != null)
        {
            tmpFilePath = await ReceiveToPartTempFileAsync(input, options, file);
        }
        if (fileResource == null)
        {
            fileResource = new FileResource
            {
                Id = id,
                FileNo = GenerateNumber(),
                Length = input.PartNumber * input.PartSize,
                OriginName = input.FileName,
                IsTemp = false,
                CreateTime = DateTime.Now,
                PeriodMinute = 0,
                Path = tmpFilePath,
                StorageKey = input.StorageKey,
                ProviderType = "local"
            };
        }

        return fileResource;
    }

    /// <summary>
    /// 生成编号
    /// </summary>
    /// <returns></returns>
    public static string GenerateNumber()
    {
        var dateTimeStr = DateTime.Now.ToString("yyyyMMddHHmmssffff");
        var random = new Random(BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0));
        var ranInt = random.Next(1000, 9999);
        return $"{dateTimeStr}{ranInt}";
    }

    /// <summary>
    /// 接收临时文件
    /// </summary>
    /// <param name="file"></param>
    /// <param name="options"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    private static async Task<string> ReceiveToTempFileAsync(Stream file, Abstractions.Models.Options.FileServerOptions options)
    {
        var filePath = GetTempFileName(options);
        try
        {
            using (var stream = File.OpenWrite(filePath))
            {
                await file.CopyToAsync(stream);
            }
        }
        catch (Exception e)
        {
            TryDelete(filePath);
            throw e;
        }

        return filePath;
    }

    /// <summary>
    /// 接收临时文件
    /// </summary>
    /// <param name="file"></param>
    /// <param name="options"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    private static async Task<string> ReceiveToPartTempFileAsync(UploadPartInputStream input, Abstractions.Models.Options.FileServerOptions options, Stream stream)
    {
        var tmpPath = GetPartTempFilePath(input.UploadId, options);
        try
        {
            var partTmpPath = tmpPath + $"/partNumber-{input.PartNumber}--{input.FileName}";
            using (var fileStream = File.OpenWrite(partTmpPath))
            {
                await stream.CopyToAsync(fileStream);
            }
        }
        catch (Exception e)
        {
            TryDelete(tmpPath);
            throw e;
        }

        return tmpPath;
    }


    public static bool TryDelete(string filePath)
    {
        try
        {
            File.Delete(filePath);
            return true;
        }
        catch (Exception e)
        {
            return false;
        }
    }

    /// <summary>
    /// 获取临时文件名
    /// </summary>
    /// <param name="options"></param>
    /// <returns></returns>
    internal static string GetTempFileName(Abstractions.Models.Options.FileServerOptions options)
    {
        string id = Guid.NewGuid().ToString();
        string optionTmpPath = options.FileTmpPath;
        if (string.IsNullOrEmpty(optionTmpPath))
        {
            optionTmpPath = Path.GetTempPath();
        }

        string tmpPath = Path.Combine(optionTmpPath, "tmp");
        if (!Directory.Exists(tmpPath))
        {
            Directory.CreateDirectory(tmpPath);
        }

        string result = Path.Combine(tmpPath, id + ".TMP");

        return result;
    }

    /// <summary>
    /// 获取临时文件地址
    /// </summary>
    /// <param name="options"></param>
    /// <returns></returns>
    internal static string GetPartTempFilePath(string uploadId, Abstractions.Models.Options.FileServerOptions options)
    {
        string id = Guid.NewGuid().ToString();
        string optionTmpPath = options.FileTmpPath;
        if (string.IsNullOrEmpty(optionTmpPath))
        {
            optionTmpPath = Path.GetTempPath();
        }
        var filePath = Path.Combine("PartTemp", uploadId);
        string tmpPath = Path.Combine(optionTmpPath, filePath);
        if (!Directory.Exists(tmpPath))
        {
            Directory.CreateDirectory(tmpPath);
        }
        return tmpPath;
    }



    /// <summary>
    /// 移动文件到指定路径
    /// </summary>
    /// <param name="srcFilePath"></param>
    /// <param name="destFilePath"></param>
    /// <param name="fileName"></param>
    /// <param name="overrideDest"></param>
    /// <returns></returns>
    public static Task MoveToPathAsync(string srcFilePath, string destFilePath, string fileName, bool overrideDest)
    {
        return Task.Run(() =>
        {
            if (overrideDest)
            {
                if (File.Exists(destFilePath))
                {
                    File.Delete(destFilePath);
                }
            }
            File.Move(srcFilePath, Path.Combine(destFilePath, fileName));

        });
    }
}